/**
 * Title: Hyperbolic engine
 * Description: jFamilyTree Engine
 * Version: provider
 * Copyright: (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 * Update: Mon Mar 10 00:00:00 CEST 2008
 */
package jFamilyTree.control;

import java.util.List;

import jFamilyTree.model.IEdge;
import jFamilyTree.model.INode;
import jFamilyTree.model.MountPoint;

/**
 * Mounter
 * 
 * @author Bernard Bou
 */
public class Mounter
{
	/**
	 * Graft mounted node onto mounting node
	 * 
	 * @param thisMountingNode
	 *        grafting node
	 * @param thisMountedNode
	 *        grafted node
	 * @param theseEdges
	 *        edge list from mounting model
	 * @param theseMountedEdges
	 *        edge list from mounted model
	 * @return true if successful, null otherwise
	 */
	public static synchronized boolean graft(INode thisMountingNode, INode thisMountedNode, List<IEdge> theseEdges, List<IEdge> theseMountedEdges)
	{
		// REQUISITES

		// mounting node must have a parent
		INode thisMountingParent = thisMountingNode.getParent();
		if (thisMountingParent == null)
			return false;

		// mounting mountpoint must be non null
		MountPoint thisMountPoint = thisMountingNode.getMountPoint();
		if (thisMountPoint == null)
			return false;

		// mounting mountpoint must be mounting
		if (!(thisMountPoint instanceof MountPoint.Mounting))
			return false;
		MountPoint.Mounting thisMountingMountPoint = (MountPoint.Mounting) thisMountPoint;

		// mounted mountpoint must null
		if (thisMountedNode.getMountPoint() != null)
			return false;

		// ALLOCATE

		// setup mounted mountpoint
		MountPoint.Mounted thisMountedMountPoint = new MountPoint.Mounted();
		thisMountedNode.setMountPoint(thisMountedMountPoint);

		// TREE

		// tree down connect
		List<INode> theseMountingParentChildren = thisMountingParent.getChildren();
		int thisIndex = theseMountingParentChildren.indexOf(thisMountingNode);
		theseMountingParentChildren.remove(thisIndex);
		theseMountingParentChildren.add(thisIndex, thisMountedNode);

		// tree up connect
		thisMountedNode.setParent(thisMountingParent);

		// STATE

		// cross reference mounting node and mounted
		thisMountedMountPoint.theMountingNode = thisMountingNode;
		thisMountingMountPoint.theMountedNode = thisMountedNode;

		// EDGES
		thisMountedMountPoint.theMountedEdges = theseMountedEdges;
		if (theseMountedEdges != null)
			theseEdges.addAll(theseMountedEdges);

		return true;
	}

	/**
	 * Prune mounted children nodes, and remove orphaned edges
	 * 
	 * @param thisMountedNode
	 *        node
	 * @param theseEdges
	 *        edge list to scan for orphaned edges
	 * @return mounting node if successful, null otherwise
	 */
	public static synchronized INode prune(INode thisMountedNode, List<IEdge> theseEdges)
	{
		// REQUISITES

		// mounting node must have a parent
		INode thisMountedParent = thisMountedNode.getParent();
		if (thisMountedParent == null)
			return null;

		// mounted mountpoint must be non-null
		MountPoint thisMountPoint = thisMountedNode.getMountPoint();
		if (thisMountPoint == null)
			return null;

		// mounted mountpoint must not be mounting
		if (!(thisMountPoint instanceof MountPoint.Mounted))
			return null;
		MountPoint.Mounted thisMountedMountPoint = (MountPoint.Mounted) thisMountPoint;

		// mounted mountpoint must be reference a mounting node
		INode thisMountingNode = thisMountedMountPoint.theMountingNode;
		if (thisMountingNode == null)
			return null;

		// mounting mountpoint must be non null
		thisMountPoint = thisMountingNode.getMountPoint();
		if (thisMountPoint == null)
			return null;

		// mounting mountpoint must be mounting
		if (!(thisMountPoint instanceof MountPoint.Mounting))
			return null;
		MountPoint.Mounting thisMountingMountPoint = (MountPoint.Mounting) thisMountPoint;

		// mounting mountpoint must reference mounted node
		if (thisMountingMountPoint.theMountedNode != thisMountedNode)
			return null;

		// TREE CONNECT

		// tree down connect
		List<INode> theseMountedParentChildren = thisMountedParent.getChildren();
		int thisIndex = theseMountedParentChildren.indexOf(thisMountedNode);
		theseMountedParentChildren.remove(thisIndex);
		theseMountedParentChildren.add(thisIndex, thisMountingNode);

		// tree up connect
		thisMountingNode.setParent(thisMountedParent);

		// STATE

		// cross reference mounting node and mounted
		thisMountedMountPoint.theMountingNode = null;
		thisMountingMountPoint.theMountedNode = null;

		// EDGES
		if (theseEdges != null)
		{
			if (thisMountedMountPoint.theMountedEdges != null)
				theseEdges.removeAll(thisMountedMountPoint.theMountedEdges);
			removeSubtreeEdges(theseEdges, thisMountedNode);
		}

		// FREE

		// dispose mounted node mountpoint
		thisMountedNode.setMountPoint(null);

		return thisMountingNode;
	}

	static private void removeSubtreeEdges(List<IEdge> theseEdges, INode thisMountedNode)
	{
		for (INode thisChildNode : thisMountedNode.getChildren())
		{
			// if mounted mount point having edges
			MountPoint thisMountPoint = thisChildNode.getMountPoint();
			if (thisMountPoint != null && thisMountPoint instanceof MountPoint.Mounted)
			{
				MountPoint.Mounted thisMountedMountPoint = (MountPoint.Mounted) thisMountPoint;
				if (thisMountedMountPoint.theMountedEdges != null)
					theseEdges.removeAll(thisMountedMountPoint.theMountedEdges);
			}
			// recurse
			removeSubtreeEdges(theseEdges, thisChildNode);
		}
	};
}
